/******************************************************************************
copyright(c)cguage.com
source code can be free to use and publish, but please keep header comments, thx

author:  cguage
contact: tangcraig@gmail.com
desc:    missing
date:    unknown

*******************************************************************************/
#pragma once

class LimitAppTime : public Job {
struct Counter {
    int times;
    int limit;    
    Counter(int limit) : limit(limit), times(0) {}
};

protected:
    string name() {return "LimitAppTime";}

    void onInit() {
    }

    void run(const string& strParams) {
        if (strParams.empty()) {
            run();
        } else if ("RELOAD" == strParams) {
            reload();
        }
    }

    void reload() {
        addTask("Notepad", 1);
        addTask("XLUEHostWndDefaultOwner", 5);
    }

    void run() {
        // timer plugin
        _timer.update();

        // get screen resolution
        static char title[256];
        int halfWidth  = ::GetSystemMetrics(SM_CXSCREEN) >> 1;
        int halfHeight = ::GetSystemMetrics(SM_CYSCREEN) >> 1;

        const int offx[] = {-(rand() % halfWidth), 0, rand() % halfWidth};
        const int offy[] = {-(rand() % halfHeight), 0, rand() % halfHeight};
        const int offxCnt = sizeof(offx) / sizeof(offx[0]);
        const int offyCnt = sizeof(offy) / sizeof(offy[0]);
        set<string> visitedTbl;

        // use 3 x 3 points to detect currently open windows,
        // close directly if exceeds limit times(seconds)
        for (int i = 0; i < offxCnt; ++i) {
            for (int j = 0; j < offyCnt; ++j) {
                HWND hwnd;
                int x = halfWidth + offx[i];
                int y = halfHeight + offy[j];
                string wndName = getWndInfoFromPos(
                                        halfWidth + offx[i], 
                                        halfHeight + offy[j], 
                                        hwnd);

                // avoid detect more than once per app
                if (visitedTbl.find(wndName) != visitedTbl.end()) {
                    continue;
                } else {
                    visitedTbl.insert(wndName);
                }

                // cout << "(" << x << "," << y << "): " << wndName << endl;

                // test if match with specified app in current screen
                auto iter = _nameTimesMap.find(wndName); 
                if (iter != _nameTimesMap.end()) {
                    Counter& counter = iter->second;
                    ++counter.times;
                    cout << wndName << ": " << counter.times << "/" << counter.limit << endl;

                    if (counter.times == counter.limit - 1) {
                        string text = loadResString(IDS_FORCE_CLOSE) + wndName;
                        InfoWnd::instance()->showText(text);
                    } else if (counter.times > counter.limit) {
                        ::GetWindowText(hwnd, title, sizeof(title) / sizeof(title[0]));

                        // close log (app, title, etc.)
                        LOG("CLOSE " << wndName << ":" << title);

                        // force close
                        counter.times = counter.limit;
                        ::SendMessage(hwnd, WM_CLOSE, 0, 0);

                        // reset times after a while
                        COR_BEGIN
                            COR_SLEEP(SEC_TO_MSEC(10 * 60)); // 10 min
                            auto iter = _nameTimesMap.find(wndName);
                            if (iter != _nameTimesMap.end()) {
                                iter->second.times = 0;
                                LOG("times reset. key:" << wndName);
                            }
                        COR_END(_timer)
                    }
                } // if iter
            } // for j
        } // for i

    }

    void addTask(const std::string& appClassName, int maxMinutes) {
        if (appClassName.empty() || maxMinutes < 1) {
            LOG("add task failed:" << appClassName << "," << maxMinutes);
            return;
        }

        // remove existed
        auto iter = _nameTimesMap.find(appClassName);
        if (iter != _nameTimesMap.end()) {
            _nameTimesMap.erase(iter);
        }

        _nameTimesMap.insert(make_pair(appClassName, maxMinutes * 60));
    }

    string getWndInfoFromPos(int x, int y, HWND& hwnd) {
        POINT pos = {x, y}; 
        hwnd = ::WindowFromPoint(pos);

        // get it's root window
        HWND parent;
        while ((parent = ::GetParent(hwnd)) != 0) {
            hwnd = parent;
        }

        // get root window's class name 
        static char name[64];
        ::GetClassName(hwnd, name, sizeof(name) / sizeof(name[0]));
        return name;
    }

private: 
    std::map<string, Counter> _nameTimesMap;
    CggUtil::Timer _timer;
} _end_LimitAppTime;